﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Yb
{

    public class YbLog : IDisposable
    {
        public static SolidBrush logerrbak = new SolidBrush(Color.IndianRed);
        public static SolidBrush loginfobak = new SolidBrush(Color.DeepSkyBlue);
        public static SolidBrush logplugbak = new SolidBrush(Color.LavenderBlush);

        public class log_data
        {
            public string Id;
            public string kind;
            public string Time;
            public string Message;

            public override string ToString()
            {
                return $"ID: {Id} Kind: {kind} Time: {Time} Message: {Message}";
            }
        }

        private static string _logDirectory;
        private readonly Action<log_data> _onLogAdded;

        private readonly BlockingCollection<log_data> _logQueue;
        private readonly Task _processingTask;
        private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
        private readonly Timer _flushTimer;

        private readonly ConcurrentDictionary<string, FileWriterWrapper> _fileWriters = new ConcurrentDictionary<string, FileWriterWrapper>();
        private bool _disposed = false;

        private class FileWriterWrapper
        {
            public StreamWriter Writer { get; set; }
            public DateTime LastUsedTime { get; set; }
            public int WriteCount { get; set; }
        }

        public static string GetDir() => _logDirectory;

        public YbLog(string path, Action<log_data> loged = null)
        {
            if (string.IsNullOrWhiteSpace(path))
                throw new ArgumentException("日志路径不能为空", nameof(path));

            _logDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, path);
            if (!Directory.Exists(_logDirectory))
                Directory.CreateDirectory(_logDirectory);

            _onLogAdded = loged;
            _logQueue = new BlockingCollection<log_data>(new ConcurrentQueue<log_data>());

            // 启动后台处理任务
            _processingTask = Task.Run(() => ProcessLogsAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token);

            // 启动定时刷新：每2秒检查是否需要强制落盘
            _flushTimer = new Timer(
                delegate { FlushAllActiveWritersAsync().Wait(); },
                null,
                TimeSpan.FromSeconds(1),
                TimeSpan.FromSeconds(1)
            );
        }

        public YbMessage Log(string message) => Log("default", message);

        public YbMessage Log(string kind, string message)
        {
            if (string.IsNullOrEmpty(message)) message = "(null)";

            var logEntry = new log_data
            {
                Id = Guid.NewGuid().ToString("N"),
                kind = string.IsNullOrEmpty(kind) ? "default" : kind,
                Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"),
                Message = message
            };

            if (_onLogAdded != null)
                _onLogAdded(logEntry);

            if (_disposed)
            {
                // 最后努力：同步写文件
                return FallbackWrite(logEntry, "日志系统已关闭");
            }

            // 尝试异步入队（等待最多 100ms）
            try
            {
                if (_logQueue.TryAdd(logEntry, 100, _cancellationTokenSource.Token))
                {
                    return new YbMessage(0, "ok");
                }
            }
            catch (OperationCanceledException)
            {
                // 已取消
            }

            // 队列满或取消 → 降级为同步写入
            return FallbackWrite(logEntry, "队列满，降级写入");
        }

        private YbMessage FallbackWrite(log_data logEntry, string reason)
        {
            try
            {
                WriteToFileSync(logEntry);
                return new YbMessage(0, "ok (" + reason + ")");
            }
            catch (Exception ex)
            {
                return new YbMessage(1, "日志写入失败: " + ex.Message);
            }
        }

        private async Task ProcessLogsAsync(CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                try
                {
                    var batch = new List<log_data>();
                    while (batch.Count < 100 && !cancellationToken.IsCancellationRequested)
                    {
                        log_data item;
                        bool success = _logQueue.TryTake(out item, 50, cancellationToken);
                        if (success)
                        {
                            batch.Add(item);
                        }
                        else
                        {
                            break;
                        }
                    }

                    if (batch.Count > 0)
                    {
                        await ProcessBatchAsync(batch);
                    }
                    else
                    {
                        await Task.Delay(10, cancellationToken);
                    }
                }
                catch (OperationCanceledException)
                {
                    break;
                }
                catch (Exception)
                {
                    await Task.Delay(100, cancellationToken);
                }
            }

            // 处理剩余日志
            ProcessRemainingLogs();
        }

        private async Task ProcessBatchAsync(List<log_data> batch)
        {
            var grouped = new Dictionary<string, List<log_data>>();
            foreach (var log in batch)
            {
                string key = GetCacheKey(log);
                if (!grouped.ContainsKey(key))
                {
                    grouped[key] = new List<log_data>();
                }
                grouped[key].Add(log);
            }

            var keysToFlush = new HashSet<string>();

            foreach (var kvp in grouped)
            {
                string key = kvp.Key;
                List<log_data> logs = kvp.Value;

                FileWriterWrapper wrapper = _fileWriters.GetOrAdd(key, _ => CreateWriter(key));
                if (wrapper?.Writer == null) continue;

                StringBuilder sb = new StringBuilder();
                foreach (var log in logs)
                {
                    sb.AppendLine($"[{log.Time}] {log.Message}");
                    wrapper.LastUsedTime = DateTime.Now;
                }

                try
                {
                    await wrapper.Writer.WriteAsync(sb.ToString());
                    wrapper.WriteCount += logs.Count;
                    keysToFlush.Add(key);
                }
                catch
                {
                    // 忽略单条失败
                }
            }

            // 批量刷新到磁盘
            await FlushWritersAsync(keysToFlush);
        }

        private FileWriterWrapper CreateWriter(string cacheKey)
        {
            try
            {
                string filePath = Path.Combine(_logDirectory, cacheKey + ".log");
                FileStream fs = new FileStream(
                    filePath,
                    FileMode.Append,
                    FileAccess.Write,
                    FileShare.Read,
                    bufferSize: 4096,
                    useAsync: true);

                return new FileWriterWrapper
                {
                    Writer = new StreamWriter(fs, Encoding.UTF8) { AutoFlush = false },
                    LastUsedTime = DateTime.Now,
                    WriteCount = 0
                };
            }
            catch
            {
                return null;
            }
        }

        private string GetCacheKey(log_data logEntry)
        {
            string date = DateTime.Now.ToString("yyyy-MM-dd");
            if (string.Equals(logEntry.kind, "default", StringComparison.OrdinalIgnoreCase))
            {
                return date;
            }
            return date + "_" + logEntry.kind;
        }

        private async Task FlushWritersAsync(HashSet<string> keys)
        {
            foreach (string key in keys)
            {
                if (_fileWriters.TryGetValue(key, out FileWriterWrapper wrapper) && wrapper?.Writer != null)
                {
                    try
                    {
                        await wrapper.Writer.FlushAsync();
                        if (wrapper.Writer.BaseStream is FileStream fs)
                        {
                            fs.Flush(true); // 强制刷到磁盘
                        }
                        wrapper.WriteCount = 0;
                    }
                    catch
                    {
                        TryRebuildWriter(key);
                    }
                }
            }
        }

        private void TryRebuildWriter(string key)
        {
            if (_fileWriters.TryRemove(key, out FileWriterWrapper old))
            {
                try
                {
                    if (old.Writer != null)
                    {
                        old.Writer.Dispose();
                    }
                }
                catch { }
            }
        }

        private async Task FlushAllActiveWritersAsync()
        {
            DateTime now = DateTime.Now;
            List<string> keys = new List<string>();

            foreach (var kvp in _fileWriters)
            {
                if (kvp.Value.WriteCount > 0 && (now - kvp.Value.LastUsedTime).TotalSeconds >= 2)
                {
                    keys.Add(kvp.Key);
                }
            }

            if (keys.Count > 0)
            {
                await FlushWritersAsync(new HashSet<string>(keys));
            }
        }

        private void ProcessRemainingLogs()
        {
            while (true)
            {
                log_data log;
                bool success = _logQueue.TryTake(out log, 100);
                if (!success) break;

                try
                {
                    WriteToFileSync(log);
                }
                catch { }
            }
        }

        private void WriteToFileSync(log_data logEntry)
        {
            string filePath;
            try
            {
                filePath = GetFilePath(logEntry);
                string dir = Path.GetDirectoryName(filePath);
                if (!Directory.Exists(dir))
                    Directory.CreateDirectory(dir);
            }
            catch
            {
                // fallback 到临时目录
                string tempDir = Path.Combine(Path.GetTempPath(), "JXLOG_Fallback");
                Directory.CreateDirectory(tempDir);
                string fileName = string.IsNullOrEmpty(logEntry.kind) ? "default" : logEntry.kind;
                filePath = Path.Combine(tempDir, $"{DateTime.Now:yyyy-MM-dd}_{fileName}.log");
            }

            FileStream fs = null;
            StreamWriter writer = null;
            try
            {
                fs = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.Read, 4096, false);
                writer = new StreamWriter(fs, Encoding.UTF8);
                writer.WriteLine($"[{logEntry.Time}] {logEntry.Message}");
                writer.Flush();
                if (fs != null)
                {
                    fs.Flush(true); // 落盘
                }
            }
            catch
            {
                // 忽略
            }
            finally
            {
                if (writer != null)
                {
                    try { writer.Dispose(); } catch { }
                }
                else if (fs != null)
                {
                    try { fs.Dispose(); } catch { }
                }
            }
        }

        private string GetFilePath(log_data logEntry)
        {
            string date = DateTime.Now.ToString("yyyy-MM-dd");
            string name = string.Equals(logEntry.kind, "default", StringComparison.OrdinalIgnoreCase)
                ? date
                : $"{date}_{logEntry.kind}";
            return Path.Combine(_logDirectory, name + ".log");
        }

        public void Dispose()
        {
            if (_disposed) return;
            _disposed = true;

            _logQueue.CompleteAdding();
            _cancellationTokenSource.Cancel();

            try
            {
                if (!_processingTask.Wait(TimeSpan.FromSeconds(10)))
                {
                    // 超时 → 强制处理剩余
                    ProcessRemainingLogs();
                }
            }
            catch { }
            finally
            {
                // 强制刷新所有文件并关闭
                foreach (var kvp in _fileWriters)
                {
                    try
                    {
                        kvp.Value.Writer?.Flush();
                        if (kvp.Value.Writer?.BaseStream is FileStream fs)
                        {
                            fs.Flush(true);
                        }
                        kvp.Value.Writer?.Dispose();
                    }
                    catch { }
                }
                _fileWriters.Clear();

                try { _flushTimer?.Dispose(); } catch { }
                try { _logQueue?.Dispose(); } catch { }
                try { _cancellationTokenSource?.Dispose(); } catch { }
            }
        }

        ~YbLog()
        {
            if (!_disposed)
                Dispose();
        }
    }
}